﻿/*
 * @(#)RmbConverter.java    Jul 6, 2010
 *
 * Copyright 2009 Nyber Shanghai, Inc. All rights reserved.
 */

package com.lab.basic;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

/**
 * 数值转换为人民币.
 * 
 * @author nickevin@gmail.com
 * @version Jul 6, 2010 5:46:52 PM
 */

public class RmbConverter {

	private static final String RMB[] = { "零", "壹", "贰", "叁", "肆", "伍", "陆",
			"柒", "捌", "玖" };

	private static final String JIAO = "角";
	private static final String FEN = "分";
	private static final String YUAN = "元";
	private static final String SHI = "拾";
	private static final String BAI = "佰";
	private static final String QIAN = "仟";
	private static final String WAN = "万";
	private static final String YI = "亿";
	private static final String ZHAO = "兆";

	public static String toRmb(String input) {
		return convert(parse(input));
	}

	private static String convert(String recognized) {
		if (isDecimalFraction(recognized)) {
			return convertDecimalFraction(recognized);
		} else if (isInteger(recognized)) {
			return convertInteger(recognized);
		} else {
			return convertFloat(recognized);
		}
	}

	private static String convertFloat(String recognized) {
		String integer = processInteger(recognized);
		if (!integer.endsWith(YUAN)) {
			integer = integer + YUAN;
		}

		return integer + processDecimalFraction(recognized);
	}

	private static String convertInteger(String recognized) {
		String result = processInteger(recognized);
		if (!result.endsWith(YUAN)) {
			result = result + YUAN;
		}

		return result + "整";
	}

	private static String convertDecimalFraction(String recognized) {
		String result = processDecimalFraction(recognized);
		if (result.endsWith(JIAO)) {
			return result + "整";
		}

		return result;
	}

	private static String processDecimalFraction(String recognized) {
		StringBuilder builder = new StringBuilder();

		List<Part> parts = buildDecimalFractionParts(recognized);
		for (Part part : parts) {
			String number = part.getNumber();
			if (!"0".equals(number)) {
				String rmb = RMB[Integer.parseInt(number)];
				builder.append(rmb);

				switch (part.getIndex()) {
				case -1:
					builder.append(JIAO);
					break;
				case -2:
					builder.append(FEN);
					break;
				}
			}
		}

		return builder.toString();
	}

	private static String processInteger(String recognized) {
		StringBuilder builder = new StringBuilder();

		List<Part> parts = buildIntegerParts(recognized);
		for (Part part : parts) {
			String number = part.getNumber();
			String rmb = RMB[Integer.parseInt(number)];

			if ("0".equals(part.getNumber()) && !isLastIndex(parts, part)) {
				builder.append(rmb);
			} else {
				builder.append(rmb);
				switch (part.getIndex()) {
				case 5:
					builder.append(WAN);
					break;
				case 4:
					builder.append(QIAN);
					break;
				case 3:
					builder.append(BAI);
					break;
				case 2:
					builder.append(SHI);
					break;
				default:
					builder.append(YUAN);
					break;
				}
			}
		}

		return builder.toString();
	}

	private static boolean isNextZero(List<Part> parts, Part part) {
		int index = parts.indexOf(part);
		try {
			Part next = parts.get(index + 1);

			return "0".equals(next.getNumber());
		} catch (IndexOutOfBoundsException e) {
			return false;
		}
	}

	private static boolean isLastIndex(List<Part> parts, Part part) {
		return part.getIndex() == parts.size();
	}

	private static List<Part> buildDecimalFractionParts(String recognized) {
		List<Part> list = new ArrayList<Part>(4);

		String result = StringUtils.substringAfter(recognized, ".");
		String[] items = result.split("");
		for (int i = 0; i < items.length; i++) {
			if (i != 0) { // remove empty header.
				list.add(new Part(items[i], 0 - i));
			}
					}

		return list;
	}

	private static List<Part> buildIntegerParts(String recognized) {
		List<Part> list = new ArrayList<Part>(16);

		String result = StringUtils.substringBefore(recognized, ".");
		String[] items = result.split("");
		int len = items.length;
		for (int i = 0; i < len; i++) {
			if (i != 0) {
			// reverse the index of item.
			list.add(new Part(items[i], len - i));
			}
			
		}

		return list;
	}

	/**
	 * 是否为整数.
	 * 
	 *  例: 100, 100.00, 120, 125.
	 * 
	 * @param actual
	 */
	private static boolean isInteger(String recognized) {
		return !recognized.contains(".")
				|| Integer
						.parseInt(StringUtils.substringAfter(recognized, ".")) == 0;
	}

	/**
	 * 是否为纯小数.
	 * 
	 * 小数由整数部分、小数部分和小数点组成.
	 * 整数部分是零的小数叫做纯小数. 例: 0.807、0.99、0.015、0.123456都是纯小数. 纯小数小于1.
	 * 
	 * @param actual
	 */
	private static boolean isDecimalFraction(String recognized) {
		String header = recognized.substring(0, 1);
		return recognized.contains(".")
				&& (".".equals(header) || "0".equals(header));
	}

	public static String parse(String input) {
		final String recognized = input.replaceAll("[,，]", "");
		try {
			if (Double.parseDouble(recognized) < 0) {
				throw new IllegalArgumentException("金额不能是负数!");
			}

			return recognized;
		} catch (NumberFormatException e) {
			throw new IllegalArgumentException("输入数据格式有误, 请检查！");
		}
	}

	private static class Part {

		private String number;
		private Integer index;

		public Part() {}

		public Part(String number, Integer index) {
			this.number = number;
			this.index = index;
		}

		public String getNumber() {
			return number;
		}

		public void setNumber(String number) {
			this.number = number;
		}

		public Integer getIndex() {
			return index;
		}

		public void setIndex(Integer index) {
			this.index = index;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + (index == null ? 0 : index.hashCode());
			result = prime * result + (number == null ? 0 : number.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (obj == null) {
				return false;
			}
			if (getClass() != obj.getClass()) {
				return false;
			}
			final Part other = (Part) obj;
			if (index == null) {
				if (other.index != null) {
					return false;
				}
			} else if (!index.equals(other.index)) {
				return false;
			}
			if (number == null) {
				if (other.number != null) {
					return false;
				}
			} else if (!number.equals(other.number)) {
				return false;
			}
			return true;
		}

		@Override
		public String toString() {
			return number + ": " + index;
		}
	}
}
